﻿// utf8-BOM模板
#include "OiPCH.hpp"
#include "OiDefinitionTemplDlg.hpp"
#include <QPainterPath>
#include "Math/OiOptimize.hpp"


namespace Oi
{
    namespace PLScannerCore
    {
        namespace
        {
            QDoubleSpinBox* lengthBox(QWidget* w)
            {
                QDoubleSpinBox* box = new QDoubleSpinBox(w);
                box->setMinimum(0);
                box->setMaximum(10000);
                box->setSuffix("mm");
                box->setValue(0);
                return box;
            }

            QDoubleSpinBox* angleBox(QWidget* w)
            {
                QDoubleSpinBox* box = new QDoubleSpinBox(w);
                box->setMinimum(0);
                box->setMaximum(360);
                box->setSuffix("°");
                box->setValue(0);
                return box;
            }

            float normQPointF(const QPointF& p)
            {
                double x = p.x();
                double y = p.y();
                double v = std::sqrt(x * x + y * y);
                return static_cast<float>(v);
            }

            QPointF verticalVector(const QPointF& dir, int quadrant)
            {
                double a = dir.x();
                double b = dir.y();
                double x = std::sqrt(1.0 / (1.0 + (a * a) / (b * b)));
                double y = std::sqrt(1.0 / (1.0 + (b * b) / (a * a)));
                if (quadrant == 1)
                {
                    return QPointF(x, y);
                }
                else if (quadrant == 2)
                {
                    return QPointF(-x, y);
                }
                else if (quadrant == 3)
                {
                    return QPointF(-x, -y);
                }
                else
                {
                    return QPointF(x, -y);
                }
            }

            QPointF rotateVector(const QPointF& dir, double angle, bool flag)
            {
                double rad = (angle * M_PI) / 180;
                QPointF res;
                if (flag)
                {
                    float x = static_cast<float>(dir.x() * std::cos(rad) + dir.y() * std::sin(rad));
                    float y = static_cast<float>(-dir.x() * std::sin(rad) + dir.y() * std::cos(rad));
                    res = QPointF(x, y);
                }
                else
                {
                    float x = static_cast<float>(dir.x() * std::cos(rad) - dir.y() * std::sin(rad));
                    float y = static_cast<float>(dir.x() * std::sin(rad) + dir.y() * std::cos(rad));
                    res = QPointF(x, y);
                }
                res /= normQPointF(res);
                return res;
            }

            QPointF findCircleCenter(const QPointF& a, const QPointF& b, double r)
            {
                double d2 = (a.x() - b.x()) * (a.x() - b.x()) + (a.y() - b.y()) * (a.y() - b.y());
                double d = std::sqrt(d2);
                if (d > std::abs(2 * r))
                {
                    return QPointF();
                }
                else if (d == std::abs(2 * r))
                {
                    return (a + b) / 2.0;
                }
                else
                {
                    QPointF m = (a + b) / 2.0;
                    QPointF AB = b - a;
                    double a2 = AB.x() * AB.x();
                    double b2 = AB.y() * AB.y();
                    double srcX = std::sqrt(1.0 / (1 + a2 / b2));
                    double srcY = std::sqrt(1.0 / (1 + b2 / a2));
                    double x1 = 0;
                    double y1 = 0;
                    double x2 = 0;
                    double y2 = 0;
                    if ((AB.x() > 0 && AB.y() > 0) || (AB.x() < 0 && AB.y() < 0))
                    {
                        x1 = -srcX;
                        y1 = srcY;
                        x2 = srcX;
                        y2 = -srcY;
                    }
                    else
                    {
                        x1 = srcX;
                        y1 = srcY;
                        x2 = -srcX;
                        y2 = -srcY;
                    }
                    double h = std::sqrt(r * r - d2 / 4.0);
                    QPointF c1 = m + QPointF(x1, y1) * h;
                    QPointF c2 = m + QPointF(x2, y2) * h;
                    QPointF dir(0, 1);
                    QPointF mc1 = m - c1;
                    QPointF mc2 = m - c2;
                    float v1 = QPointF::dotProduct(mc1, dir);
                    float v2 = QPointF::dotProduct(mc2, dir);
                    if (r > 0)
                    {
                        if (v1 > 0)
                        {
                            return c1;
                        }
                        else
                        {
                            return c2;
                        }
                    }
                    else
                    {
                        if (v1 < 0)
                        {
                            return c1;
                        }
                        else
                        {
                            return c2;
                        }
                    }
                }
            }


            class Bezier
            {
            public:
                static Bezier fromPoints(const QPointF& p1, const QPointF& p2, const QPointF& p3, const QPointF& p4)
                {
                    return { p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y(), p4.x(), p4.y() };
                }

                QPointF pt1() const { return QPointF(x1, y1); }
                QPointF pt2() const { return QPointF(x2, y2); }
                QPointF pt3() const { return QPointF(x3, y3); }
                QPointF pt4() const { return QPointF(x4, y4); }

                inline QPointF pointAt(qreal t) const
                {
                    // numerically more stable:
                    qreal x, y;

                    qreal m_t = 1. - t;
                    {
                        qreal a = x1 * m_t + x2 * t;
                        qreal b = x2 * m_t + x3 * t;
                        qreal c = x3 * m_t + x4 * t;
                        a = a * m_t + b * t;
                        b = b * m_t + c * t;
                        x = a * m_t + b * t;
                    }
                    {
                        qreal a = y1 * m_t + y2 * t;
                        qreal b = y2 * m_t + y3 * t;
                        qreal c = y3 * m_t + y4 * t;
                        a = a * m_t + b * t;
                        b = b * m_t + c * t;
                        y = a * m_t + b * t;
                    }
                    return QPointF(x, y);
                }

                qreal length(qreal error = 0.01) const
                {
                    qreal length = qreal(0.0);

                    addIfClose(&length, error);

                    return length;
                }

                void addIfClose(qreal* length, qreal error) const
                {
                    qreal len = qreal(0.0);  /* arc length */
                    qreal chord;             /* chord length */

                    len = len + QLineF(QPointF(x1, y1), QPointF(x2, y2)).length();
                    len = len + QLineF(QPointF(x2, y2), QPointF(x3, y3)).length();
                    len = len + QLineF(QPointF(x3, y3), QPointF(x4, y4)).length();

                    chord = QLineF(QPointF(x1, y1), QPointF(x4, y4)).length();

                    if ((len - chord) > error)
                    {
                        const auto halves = split();                  /* split in two */
                        halves.first.addIfClose(length, error);       /* try left side */
                        halves.second.addIfClose(length, error);      /* try right side */
                        return;
                    }

                    *length = *length + len;

                    return;
                }

                std::pair<Bezier, Bezier> split() const
                {
                    const auto mid = [](QPointF lhs, QPointF rhs) { return (lhs + rhs) * .5; };

                    const QPointF mid_12 = mid(pt1(), pt2());
                    const QPointF mid_23 = mid(pt2(), pt3());
                    const QPointF mid_34 = mid(pt3(), pt4());
                    const QPointF mid_12_23 = mid(mid_12, mid_23);
                    const QPointF mid_23_34 = mid(mid_23, mid_34);
                    const QPointF mid_12_23__23_34 = mid(mid_12_23, mid_23_34);

                    return {
                        fromPoints(pt1(), mid_12, mid_12_23, mid_12_23__23_34),
                        fromPoints(mid_12_23__23_34, mid_23_34, mid_34, pt4()),
                    };
                }

            public:
                qreal x1, y1, x2, y2, x3, y3, x4, y4;
            };


            class DxfMath
            {
            public:
                //get fitPois for spline with NURBS, ignore knotWeights
                static int getKnotSpan(Base::List<Math::Point3d>& controlPs, Base::List<double> knots, int degree, double u)
                { // alg A2.1 from p.68 of "The NURBS Book"
                    try {
                        int n = controlPs.size() - 1;
                        int low, mid, hi;
                        low = degree;
                        hi = n + 1;
                        mid = floor((low + hi) / 2);

                        if (u == knots[hi])
                        {
                            return (n);
                        }

                        // a binary search to find the knotspan index
                        while (u < knots[mid] || u >= knots[mid + 1]) {
                            if (u < knots[mid]) {
                                hi = mid;
                            }
                            else {
                                low = mid;
                            }
                            mid = floor((low + hi) / 2);
                        }
                        return mid;
                    }
                    catch (const std::exception& e) { // reference to the base of a polymorphic object
                        OiWarning() << "  " << e.what() << " error in getKnotSpan()\n";
                        return -1;
                    }
                }

                static int basisFuns(Base::List<Math::Point3d>& controlPs, Base::List<double> knots, double u, int knotSpan, int degree, Base::List<double>& returnBasis)
                {
                    int n = controlPs.size() - 1;
                    try {
                        int i = knotSpan;
                        returnBasis.clear();
                        returnBasis.resize(n + 1, 0.0);
                        returnBasis[0] = 1;
                        Base::List<double> left;
                        left.resize(n + 1, 0.0);
                        Base::List<double> right;
                        right.resize(n + 1, 0.0);

                        for (int j = 1; j <= degree; j++) {
                            left[j] = u - knots[i + 1 - j];
                            right[j] = knots[i + j] - u;
                            double saved = 0.0;
                            for (int r = 0; r < j; r++) {
                                double temp = returnBasis[r] / (right[r + 1] + left[j - r]);
                                returnBasis[r] = saved + right[r + 1] * temp;
                                saved = left[j - r] * temp;
                            }
                            returnBasis[j] = saved;
                        }
                        return 1;
                    }
                    catch (const std::exception& e) { // reference to the base of a polymorphic object
                        OiWarning() << "  " << e.what() << " error.\n";
                        return 0;
                    }
                }

                static int getPoint(Base::List<Math::Point3d>& controlPs, Base::List<double> knots, int degree, double u, Math::Point3d& point)
                {
                    try {
                        int span = getKnotSpan(controlPs, knots, degree, u);

                        if (span == -1)
                        {
                            return 0;
                        }

                        Base::List<double> basis;
                        basisFuns(controlPs, knots, u, span, degree, basis);

                        for (int i = 0; i <= degree; i++) {
                            point.x() += basis[i] * controlPs[span - degree + i].x();
                            point.y() += basis[i] * controlPs[span - degree + i].y();
                            point.z() += basis[i] * controlPs[span - degree + i].z();
                        }
                        return 1;
                    }
                    catch (const std::exception& e) { // reference to the base of a polymorphic object
                        OiWarning() << "  " << e.what() << " error.\n";
                        return 0;
                    }
                }

                static int getPoints(Base::List<Math::Point3d>& controlPs, Base::List<double> knots, Base::List<double> weights, int degree, int divisions, Base::List<Math::Point3d>& curvePs)
                {
                    curvePs.clear();

                    if (knots.size() != controlPs.size() + degree + 1)
                    {
                        return 0;
                    }

                    curvePs.resize(divisions + 1);
                    auto minmax = std::minmax_element(knots.begin(), knots.end());
                    double stepSize = (*minmax.second - *minmax.first) / divisions;
                    int vecInd = 0;
                    for (double i = 0; i < *minmax.second; i += stepSize) {
                        if (vecInd > curvePs.size() - 1) {
                            break; // prevent overrun caused by binary arithmetic errors
                        }
                        Math::Point3d tempPoi(0, 0, 0);
                        if (getPoint(controlPs, knots, degree, i, tempPoi)) {
                            curvePs[vecInd] = tempPoi; // copies a shared_ptr to a new point into the return vector
                            vecInd++;
                        }
                        else {
                            return 0;
                        }
                    }
                    // this puts the last point directly at the curve endpoint, avoiding arithmetic errors
                    Math::Point3d tempPoi(0, 0, 0);
                    if (getPoint(controlPs, knots, degree, knots[knots.size() - 1], tempPoi)) {// use the last entry in the knot vector
                        curvePs[curvePs.size() - 1] = tempPoi;
                        return 1; // copies a shared_ptr to a new point into the return vector
                    }
                    else {
                        return 0;
                    }
                    return 0; // should have returned in the above conditional
                }
                //end NURBS


                static float distance(const QPointF& p1, const QPointF& p2)
                {
                    //return qSqrt(qPow(p1.x() - p2.x(), 2) + qPow(p1.y() - p2.y(), 2));
                    return (p1 - p2).manhattanLength();
                }

                static Base::List<std::tuple<qreal, qreal, Bezier>> beziers(const QPainterPath& path, qreal error = 0.01)
                {
                    Base::List<std::tuple<qreal, qreal, Bezier>> beziers;

                    qreal curLen = 0;
                    const int lastElement = path.elementCount() - 1;
                    for (int i = 0; i <= lastElement; ++i)
                    {
                        const QPainterPath::Element& e = path.elementAt(i);

                        switch (e.type) {
                        case QPainterPath::MoveToElement:
                            break;
                        case QPainterPath::LineToElement:
                        {
                            QLineF line(path.elementAt(i - 1), e);
                            qreal llen = line.length();
                            curLen += llen;
                            QPointF a = path.elementAt(i - 1);
                            QPointF delta = e - a;
                            beziers.push_back(std::make_tuple(curLen, llen, Bezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e)));
                            break;
                        }
                        case QPainterPath::CurveToElement:
                        {
                            Bezier b = Bezier::fromPoints(path.elementAt(i - 1),
                                e,
                                path.elementAt(i + 1),
                                path.elementAt(i + 2));
                            qreal blen = b.length(error);
                            curLen += blen;
                            beziers.push_back(std::make_tuple(curLen, blen, b));
                            i += 2;
                            break;
                        }
                        default:
                            break;
                        }
                    }

                    return beziers;
                }

                static Bezier bezierAtT(const QPainterPath& path, qreal t, qreal* startingLength, qreal* bezierLength, const Base::List<std::tuple<qreal, qreal, Bezier>>& beziers, qreal totalLength = -1)
                {
                    *startingLength = 0;

                    if (t > 1) return Bezier();

                    qreal curLen = 0;
                    if (totalLength < 0)
                    {
                        totalLength = path.length();
                    }

                    for (int i = 0; i < beziers.size(); i++)
                    {
                        curLen = std::get<0>(beziers[i]);
                        if (i + 1 == beziers.size() || curLen / totalLength >= t)
                        {
                            *bezierLength = std::get<1>(beziers[i]);
                            return std::get<2>(beziers[i]);
                        }
                        *startingLength = curLen;
                    }

                    return Bezier();
                }

            };


            QList<QPolygonF> pathPolygons(const QPainterPath& path, double pointDistance)
            {
                Base::List<QPointF> basePositions;
                for (int i = 0; i < path.elementCount(); i++)
                {
                    basePositions.push_back(QPointF(path.elementAt(i).x, path.elementAt(i).y));
                }

                QList<QPolygonF> combinePolygons;

                auto pathLength = path.length();
                int stepNum = pathLength / pointDistance;
                QPolygonF fullPolygon;
                Base::List<QPolygonF> fullPolygons;
                int lastCloseIndex = -1;
                Base::List<QPointF> percentPoints;
                percentPoints.resize(stepNum);

                auto beziers = DxfMath::beziers(path, pointDistance / 3);

                #pragma omp parallel for schedule(static)
                for (int i = 0; i < stepNum; i++)
                {
                    qreal totalLength = path.length();
                    qreal curLen = 0;
                    qreal bezierLen = 0;
                    float t = float(i) / stepNum;
                    Bezier b = DxfMath::bezierAtT(path, t, &curLen, &bezierLen, beziers, pathLength);
                    qreal realT = (totalLength * t - curLen) / bezierLen;
                    percentPoints[i] = b.pointAt(qBound(qreal(0), realT, qreal(1)));
                }

                for (int i = 0; i < stepNum; i++)
                {
                    QPointF p = percentPoints[i];
                    int closeType = 0;
                    if (lastCloseIndex != -1)
                    {
                        if (DxfMath::distance(p, fullPolygons[lastCloseIndex].back()) < pointDistance * 2)
                        {
                            closeType = 1;
                        }
                        else if (DxfMath::distance(p, fullPolygons[lastCloseIndex].front()) < pointDistance * 2)
                        {
                            closeType = 2;
                        }
                    }

                    if (closeType == 0)
                    {
                        fullPolygons.push_back(QPolygonF() << p);
                        lastCloseIndex = fullPolygons.size() - 1;
                    }
                    else if (closeType == 1)
                    {
                        fullPolygons[lastCloseIndex].push_back(p);
                    }
                    else if (closeType == 2)
                    {
                        fullPolygons[lastCloseIndex].insert(fullPolygons[lastCloseIndex].begin(), p);
                    }
                }

                if (fullPolygons.empty())
                {
                    return combinePolygons;
                }

                QMap<int, int> takeRecords;
                while (takeRecords.size() != fullPolygons.size())
                {
                    QPolygonF fullPolygon;
                    for (int i = 0; i < fullPolygons.size(); i++)
                    {
                        if (takeRecords.contains(i))
                        {
                            continue;
                        }

                        fullPolygon = fullPolygons[i];
                        takeRecords[i] = i;
                        break;
                    }

                    for (int i = 0; i < fullPolygons.size(); i++)
                    {
                        int closeIndex = -1;
                        float closeDistance = 1e10;
                        int closeType = 0;
                        for (int j = 0; j < fullPolygons.size(); j++)
                        {
                            if (takeRecords.contains(j))
                            {
                                continue;
                            }

                            QPolygonF& currPolygon = fullPolygons[j];
                            Base::List<std::pair<int, float>> distancePair;
                            distancePair.push_back(std::make_pair(0, DxfMath::distance(currPolygon.front(), fullPolygon.back())));
                            distancePair.push_back(std::make_pair(1, DxfMath::distance(currPolygon.front(), fullPolygon.front())));
                            distancePair.push_back(std::make_pair(2, DxfMath::distance(currPolygon.back(), fullPolygon.back())));
                            distancePair.push_back(std::make_pair(3, DxfMath::distance(currPolygon.back(), fullPolygon.front())));
                            std::sort(distancePair.begin(), distancePair.end(), [](auto d1, auto d2) { return d1.second < d2.second; });

                            if (distancePair.front().second < closeDistance)
                            {
                                closeDistance = distancePair.front().second;
                                closeIndex = j;
                                closeType = distancePair.front().first;
                            }
                        }

                        if (closeIndex != -1 && closeDistance < pointDistance * 20)
                        {
                            takeRecords[closeIndex] = closeIndex;
                            QPolygonF& currPolygon = fullPolygons[closeIndex];
                            if (closeType == 0)
                            {
                                fullPolygon << currPolygon;
                            }
                            else if (closeType == 1)
                            {
                                for (auto iter = currPolygon.begin(); iter != currPolygon.end(); iter++)
                                {
                                    fullPolygon.insert(0, *iter);
                                }
                            }
                            else if (closeType == 2)
                            {
                                for (auto iter = currPolygon.rbegin(); iter != currPolygon.rend(); iter++)
                                {
                                    fullPolygon.push_back(*iter);
                                }
                            }
                            else if (closeType == 3)
                            {
                                fullPolygon = currPolygon + fullPolygon;
                            }
                        }
                    }

                    if (fullPolygon.size() > 10)
                    {
                        Base::List<cv::Point2f> interestPoints;
                        for (int i = 0; i < fullPolygon.size(); i++)
                        {
                            interestPoints.push_back(cv::Point2f(fullPolygon[i].x(), fullPolygon[i].y()));
                        }
                        for (int i = 0; i < basePositions.size(); i++)
                        {
                            interestPoints.push_back(cv::Point2f(basePositions[i].x(), basePositions[i].y()));
                        }
                        Base::List<cv::Point2f> orderlyProfile = Math::orderDiscreteProfile(interestPoints, 16);

                        fullPolygon.clear();
                        for (int i = 0; i < orderlyProfile.size(); i++)
                        {
                            fullPolygon.push_back(QPointF(orderlyProfile[i].x, orderlyProfile[i].y));
                        }
                        fullPolygon.push_back(fullPolygon.front());
                    }

                    combinePolygons.push_back(fullPolygon);
                }

                return combinePolygons;
            }


        }

        class DefinitionTemplDlgImpl :public Impl<DefinitionTemplDlg>
        {
        public:
            void init(DefinitionType dt);
            void exit();
            TemplInputWidget* createInputWidget(DefinitionType dt);
            QLabel* createTemplImg(DefinitionType dt);
            QDoubleSpinBox* lengthBox(QWidget* w);
            QDoubleSpinBox* angleBox(QWidget* w);

        public:
            QBoxLayout* _mainLay = nullptr;
            QHBoxLayout* _wrapLay = nullptr;
            QLabel* _templImg = nullptr;
            QHBoxLayout* _btnLay = nullptr;
            QPushButton* _btnConfirm = nullptr;
            QPushButton* _btnCancel = nullptr;
            TemplInputWidget* _inputWidget = nullptr;

            DefinitionType _dt = DT_Mutational;
            ProductParams _params;
        };

        void DefinitionTemplDlgImpl::init(DefinitionType dt)
        {
            OI_Q(DefinitionTemplDlg);
            _dt = dt;

            _mainLay = new QVBoxLayout(q);
            q->setLayout(_mainLay);

            _wrapLay = new QHBoxLayout(q);
            _inputWidget = createInputWidget(dt);
            _templImg = createTemplImg(dt);
            _wrapLay->addWidget(_inputWidget);
            _wrapLay->addWidget(_templImg);
            _mainLay->addLayout(_wrapLay);

            _btnLay = new QHBoxLayout(q);
            _btnConfirm = new QPushButton(DefinitionTemplDlg::tr("确认"), q);
            _btnCancel = new QPushButton(DefinitionTemplDlg::tr("取消"), q);
            _btnLay->addSpacerItem(new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Maximum));
            _btnLay->addWidget(_btnConfirm);
            _btnLay->addWidget(_btnCancel);
            _mainLay->addLayout(_btnLay);

            q->connect(_btnConfirm, &QPushButton::clicked, q, &DefinitionTemplDlg::onConfirm);
            q->connect(_btnCancel, &QPushButton::clicked, q, &DefinitionTemplDlg::onCancel);

            q->resize(1220, 700);
        }

        void DefinitionTemplDlgImpl::exit()
        {

        }

        TemplInputWidget* DefinitionTemplDlgImpl::createInputWidget(DefinitionType dt)
        {
            OI_Q(DefinitionTemplDlg);
            TemplInputWidget* w = nullptr;
            switch (dt)
            {
            case Oi::PLScannerCore::DT_TempleStandLine:
            case Oi::PLScannerCore::DT_TempleStandLineMark:
            {
                w = new TemplTypeStandLine(dt, q);
                break;
            }
            case Oi::PLScannerCore::DT_TempleReveseLie:
            case Oi::PLScannerCore::DT_TempleReveseLieMark:
            {
                w = new TemplTypeReveseLie(dt, q);
                break;
            }
            case Oi::PLScannerCore::DT_TempleStandPart:
            case Oi::PLScannerCore::DT_TempleStandPartMark:
            {
                w = new TemplTypeStandPart(dt, q);
                break;
            }
            case Oi::PLScannerCore::DT_TempleStandArcs:
            case Oi::PLScannerCore::DT_TempleStandArcsMark:
            {
                w = new TemplTypeStandArcs(dt, q);
                break;
            }
            default: {
                w = new TemplInputWidget(dt, q);
                break;
            }
            }
            return w;
        }

        QLabel* DefinitionTemplDlgImpl::createTemplImg(DefinitionType dt)
        {
            OI_Q(DefinitionTemplDlg);
            QLabel* b = new QLabel(q);
            b->setMaximumSize(800, 600);
            b->setScaledContents(true);
            switch (dt)
            {
            case Oi::PLScannerCore::DT_TempleStandLine:
            case Oi::PLScannerCore::DT_TempleStandLineMark:
            {
                QPixmap pixmap(":/Flormor/TypeA.png");
                b->setPixmap(pixmap);
                break;
            }
            case Oi::PLScannerCore::DT_TempleReveseLie:
            case Oi::PLScannerCore::DT_TempleReveseLieMark:
            {
                QPixmap pixmap(":/Flormor/TypeB.png");
                b->setPixmap(pixmap);
                break;
            }
            case Oi::PLScannerCore::DT_TempleStandPart:
            case Oi::PLScannerCore::DT_TempleStandPartMark:
            {
                QPixmap pixmap(":/Flormor/TypeC.png");
                b->setPixmap(pixmap);
                break;
            }
            case Oi::PLScannerCore::DT_TempleStandArcs:
            case Oi::PLScannerCore::DT_TempleStandArcsMark:
            {
                QPixmap pixmap(":/Flormor/TypeD.png");
                b->setPixmap(pixmap);
                break;
            }
            default: {
                QPixmap pixmap(":/Flormor/UnkownType.png");
                b->setPixmap(pixmap);
                break;
            }
            }
            return b;
        }

        QDoubleSpinBox* DefinitionTemplDlgImpl::lengthBox(QWidget* w)
        {
            QDoubleSpinBox* box = new QDoubleSpinBox(w);
            box->setMinimum(0);
            box->setMaximum(10000);
            box->setSuffix("mm");
            box->setValue(0);
            return box;
        }

        QDoubleSpinBox* DefinitionTemplDlgImpl::angleBox(QWidget* w)
        {
            QDoubleSpinBox* box = new QDoubleSpinBox(w);
            box->setMinimum(0);
            box->setMaximum(360);
            box->setSuffix("°");
            box->setValue(0);
            return box;
        }

        DefinitionTemplDlg::DefinitionTemplDlg(DefinitionType dt, QWidget* parent)
            :QDialog(parent)
        {
            OI_I(DefinitionTemplDlg)->init(dt);
        }

        DefinitionTemplDlg::~DefinitionTemplDlg()
        {
            OI_E(DefinitionTemplDlg)->exit();
        }

        void DefinitionTemplDlg::onConfirm()
        {
            accept();
        }

        void DefinitionTemplDlg::onCancel()
        {
            reject();
        }

        void DefinitionTemplDlg::setParams(const ProductParams& params)
        {
            OI_F(DefinitionTemplDlg);
            d->_params = params;
            d->_inputWidget->setParams(params);
        }

        const ProductParams& DefinitionTemplDlg::params()
        {
            OI_F(DefinitionTemplDlg);
            d->_inputWidget->updateParams(d->_params);
            return d->_params;
        }

        class TemplInputWidgetImpl :public Impl<TemplInputWidget>
        {
        public:
            void init();
            void exit();

        public:
            DefinitionType _dt;
        };

        void TemplInputWidgetImpl::init()
        {
        }

        void TemplInputWidgetImpl::exit()
        {
        }

        TemplInputWidget::TemplInputWidget(DefinitionType dt, QWidget* parent)
            :QWidget(parent)
        {
            OI_I(TemplInputWidget)->_dt = dt;
        }

        TemplInputWidget::~TemplInputWidget()
        {
            OI_E(TemplInputWidget);
        }

        QList<DefinitionPoint> TemplInputWidget::definitionPoints()
        {
            return QList<DefinitionPoint>();
        }

        OiProfiles TemplInputWidget::parseProfile(const QList<DefinitionPoint>&)
        {
            return OiProfiles();
        }

        void TemplInputWidget::setParams(const ProductParams&)
        {
        }

        void TemplInputWidget::updateParams(ProductParams&)
        {
        }

        DefinitionType TemplInputWidget::getDefinitionType()
        {
            return OI_IMPL()->_dt;
        }

        class TemplTypeStandLineImpl :public Impl<TemplTypeStandLine>
        {
        public:
            void init();
            void exit();

        public:
            QFormLayout* _lay = nullptr;
            QDoubleSpinBox* _a = nullptr;
            QDoubleSpinBox* _b = nullptr;
            QDoubleSpinBox* _c = nullptr;
            QDoubleSpinBox* _d = nullptr;
            QDoubleSpinBox* _e = nullptr;
            QDoubleSpinBox* _f = nullptr;
            QDoubleSpinBox* _g = nullptr;
            QDoubleSpinBox* _h = nullptr;
            QDoubleSpinBox* _i = nullptr;
            QDoubleSpinBox* _j = nullptr;
            QDoubleSpinBox* _k = nullptr;
            QDoubleSpinBox* _l = nullptr;
        };

        void TemplTypeStandLineImpl::init()
        {
            OI_Q(TemplTypeStandLine);
            _lay = new QFormLayout(q);
            q->setLayout(_lay);
            _a = lengthBox(q);
            _lay->addRow("A", _a);
            _b = lengthBox(q);
            _lay->addRow("B", _b);
            _c = lengthBox(q);
            _lay->addRow("C", _c);
            _d = lengthBox(q);
            _lay->addRow("D", _d);
            _e = lengthBox(q);
            _lay->addRow("E", _e);
            _f = angleBox(q);
            _lay->addRow("F", _f);
            _g = lengthBox(q);
            _lay->addRow("G", _g);
            _h = lengthBox(q);
            _lay->addRow("H", _h);
            _i = lengthBox(q);
            _lay->addRow("I", _i);
            _j = angleBox(q);
            _lay->addRow("J", _j);
            _k = lengthBox(q);
            _lay->addRow("K", _k);
            _l = lengthBox(q);
            _lay->addRow("L", _l);
        }

        void TemplTypeStandLineImpl::exit()
        {

        }

        TemplTypeStandLine::TemplTypeStandLine(DefinitionType dt, QWidget* parent)
            :TemplInputWidget(dt, parent)
        {
            OI_I(TemplTypeStandLine)->init();
        }

        TemplTypeStandLine::~TemplTypeStandLine()
        {
            OI_E(TemplTypeStandLine)->exit();
        }

        QList<DefinitionPoint> TemplTypeStandLine::definitionPoints()
        {
            OI_F(TemplTypeStandLine);
            QList<DefinitionPoint> dpArr;
            DefinitionPoint dp;
            dp.type = getDefinitionType();
            dp.distance = d->_a->value();
            dpArr.append(dp);
            dp.distance = d->_b->value();
            dpArr.append(dp);
            dp.distance = d->_c->value();
            dpArr.append(dp);
            dp.distance = d->_d->value();
            dpArr.append(dp);
            dp.distance = d->_e->value();
            dpArr.append(dp);
            dp.distance = d->_f->value();
            dpArr.append(dp);
            dp.distance = d->_g->value();
            dpArr.append(dp);
            dp.distance = d->_h->value();
            dpArr.append(dp);
            dp.distance = d->_i->value();
            dpArr.append(dp);
            dp.distance = d->_j->value();
            dpArr.append(dp);
            dp.distance = d->_k->value();
            dpArr.append(dp);
            dp.distance = d->_l->value();
            dpArr.append(dp);
            return dpArr;
        }

        OiProfiles TemplTypeStandLine::parseProfile(const QList<DefinitionPoint>& dpArr)
        {
            OI_F(TemplTypeStandLine);

            QPointF p0(0, 0);

            QPointF pA= QPointF(dpArr[0].distance, 0);

            QPointF pB = QPointF(pA.x(), dpArr[1].distance);

            QPointF pJ = QPointF(-dpArr[2].distance, 0);

            double dVal = dpArr[3].distance;
            double eVal = dpArr[4].distance;
            double h = std::sqrt(eVal * eVal - dVal * dVal);
            QPointF pF = QPointF(pJ.x() + dVal, h);

            const QPointF dir1 = pJ - pF;
            const QPointF dir2 = pF - pJ;
            double fVal = (dpArr[5].distance * M_PI) / 180.0;
            QPointF dirFG = QPointF(dir1.x() * std::cos(fVal) + dir1.y() * std::sin(fVal), -dir1.x() * std::sin(fVal) + dir1.y() * std::cos(fVal));
            dirFG /= normQPointF(dirFG);
            QPointF pG = pF + dirFG * dpArr[6].distance;

            QPointF dirGH = verticalVector(dirFG, 1);
            QPointF  pH = pG + dirGH * dpArr[7].distance;

            QPointF pI = QPointF(0, dpArr[8].distance);
            double jVal = (dpArr[9].distance * M_PI) / 180.0;
            QPointF dirJK = QPointF(dir2.x() * std::cos(jVal) - dir2.y() * std::sin(jVal), dir2.x() * std::sin(jVal) + dir2.y() * std::cos(jVal));
            dirJK /= normQPointF(dirJK);
            QPointF pK = pJ + dirJK * dpArr[10].distance;

            QPointF dirKL = verticalVector(dirJK, 4);
            QPointF pL = pK + dirKL * dpArr[11].distance;

            OiProfiles res;
            QPainterPath path;
            OiProfile keyPtArr;

            path.moveTo(p0);
            keyPtArr.append(p0);
            path.lineTo(pA);
            keyPtArr.append(pA);
            path.lineTo(pB);
            keyPtArr.append(pB);
            path.lineTo(pI);
            keyPtArr.append(pI);
            path.lineTo(pH);
            keyPtArr.append(pH);
            path.lineTo(pG);
            keyPtArr.append(pG);
            path.lineTo(pF);
            keyPtArr.append(pF);
            path.lineTo(pJ);
            keyPtArr.append(pJ);
            path.lineTo(pK);
            keyPtArr.append(pK);
            path.lineTo(pL);
            keyPtArr.append(pL);
            path.lineTo(p0);

            res.append(keyPtArr);

            auto polygons = pathPolygons(path, 0.1);
            for (auto& polygon : polygons)
            {
                res.push_back(polygon.toList());
            }

            return res;
        }

        void TemplTypeStandLine::setParams(const ProductParams& params)
        {
            OI_F(TemplTypeStandLine);
            auto& dpArr = params.definitionPoints;
            if (dpArr.size() < 12)
            {
                return;
            }
            d->_a->setValue(dpArr[0].distance);
            d->_b->setValue(dpArr[1].distance);
            d->_c->setValue(dpArr[2].distance);
            d->_d->setValue(dpArr[3].distance);
            d->_e->setValue(dpArr[4].distance);
            d->_f->setValue(dpArr[5].distance);
            d->_g->setValue(dpArr[6].distance);
            d->_h->setValue(dpArr[7].distance);
            d->_i->setValue(dpArr[8].distance);
            d->_j->setValue(dpArr[9].distance);
            d->_k->setValue(dpArr[10].distance);
            d->_l->setValue(dpArr[11].distance);
        }

        void TemplTypeStandLine::updateParams(ProductParams& params)
        {
            params.definitionPoints = definitionPoints();
            params.profiles = parseProfile(params.definitionPoints);
            params.materialsThickness = 0;
            if (params.profiles.isEmpty() || params.profiles.first().isEmpty())
            {
                params.materialsWidth = 0;
            }
            else
            {
                auto& arr = params.profiles.first();
                float minVal = arr.first().x();
                float maxVal = arr.first().x();
                for (auto& val : arr)
                {
                    if (val.x() < minVal)
                    {
                        minVal = val.x();
                    }
                    if (val.x() > maxVal)
                    {
                        maxVal = val.x();
                    }
                }
                params.materialsWidth = std::abs(maxVal - minVal);
            }
        }

        class TemplTypeReveseLieImpl :public Impl<TemplTypeReveseLie>
        {
        public:
            void init();
            void exit();

        public:
            QFormLayout* _lay = nullptr;
            QDoubleSpinBox* _a = nullptr;
            QDoubleSpinBox* _b = nullptr;
            QDoubleSpinBox* _c = nullptr;
            QDoubleSpinBox* _d = nullptr;
            QDoubleSpinBox* _e = nullptr;
            QDoubleSpinBox* _f = nullptr;
            QDoubleSpinBox* _g = nullptr;
            QDoubleSpinBox* _h = nullptr;
            QDoubleSpinBox* _i = nullptr;
        };

        void TemplTypeReveseLieImpl::init()
        {
            OI_Q(TemplTypeReveseLie);
            _lay = new QFormLayout(q);
            q->setLayout(_lay);
            _a = lengthBox(q);
            _lay->addRow("A", _a);
            _b = lengthBox(q);
            _lay->addRow("B", _b);
            _c = angleBox(q);
            _lay->addRow("C", _c);
            _d = lengthBox(q);
            _lay->addRow("D", _d);
            _e = lengthBox(q);
            _lay->addRow("E", _e);
            _f = lengthBox(q);
            _lay->addRow("F", _f);
            _g = angleBox(q);
            _lay->addRow("G", _g);
            _h = lengthBox(q);
            _lay->addRow("H", _h);
            _i = lengthBox(q);
            _lay->addRow("I", _i);
        }

        void TemplTypeReveseLieImpl::exit()
        {

        }

        TemplTypeReveseLie::TemplTypeReveseLie(DefinitionType dt, QWidget* parent)
            :TemplInputWidget(dt, parent)
        {
            OI_I(TemplTypeReveseLie)->init();
        }

        TemplTypeReveseLie::~TemplTypeReveseLie()
        {
            OI_E(TemplTypeReveseLie)->exit();
        }

        QList<DefinitionPoint> TemplTypeReveseLie::definitionPoints()
        {
            OI_F(TemplTypeReveseLie);

            QList<DefinitionPoint> dpArr;
            DefinitionPoint dp;
            dp.type = getDefinitionType();
            dp.distance = d->_a->value();
            dpArr.append(dp);
            dp.distance = d->_b->value();
            dpArr.append(dp);
            dp.distance = d->_c->value();
            dpArr.append(dp);
            dp.distance = d->_d->value();
            dpArr.append(dp);
            dp.distance = d->_e->value();
            dpArr.append(dp);
            dp.distance = d->_f->value();
            dpArr.append(dp);
            dp.distance = d->_g->value();
            dpArr.append(dp);
            dp.distance = d->_h->value();
            dpArr.append(dp);
            dp.distance = d->_i->value();
            dpArr.append(dp);
            return dpArr;
        }

        OiProfiles TemplTypeReveseLie::parseProfile(const QList<DefinitionPoint>& dpArr)
        {
            OI_F(TemplTypeReveseLie);
            OiProfiles res;
            QPainterPath path;
            OiProfile keyPtArr;

            QPointF p0(0, 0);
            double aVal = d->_a->value();
            QPointF p1(aVal, 0);

            double bVal = d->_b->value();
            QPointF p2(aVal, bVal);

            double eVal = d->_e->value();
            double fVal = d->_f->value();
            QPointF p3(eVal, fVal);

            QPointF dir01 = p1 - p0;
            double cVal = (d->_c->value() * M_PI) / 180.0;
            QPointF dir0d(dir01.x() * std::cos(cVal) - dir01.y() * std::sin(cVal), dir01.x() * std::sin(cVal) + dir01.y() * std::cos(cVal));
            dir0d /= normQPointF(dir0d);
            QPointF pD = p0 + dir0d * d->_d->value();

            QPointF dird0 = -dir0d;
            double gVal = (d->_g->value() * M_PI) / 180.0;
            QPointF dirDH(dird0.x() * std::cos(gVal) + dird0.y() * std::sin(gVal), -dird0.x() * std::sin(gVal) + dird0.y() * std::cos(gVal));
            dirDH /= normQPointF(dirDH);
            QPointF pH = pD + dirDH * d->_h->value();

            QPointF dirHI(dirDH.y(), -dirDH.x());
            QPointF pI = pH + dirHI * d->_i->value();

            path.moveTo(p0);
            keyPtArr.append(p0);
            path.lineTo(p1);
            keyPtArr.append(p1);
            path.lineTo(p2);
            keyPtArr.append(p2);
            path.lineTo(p3);
            keyPtArr.append(p3);
            path.lineTo(pI);
            keyPtArr.append(pI);
            path.lineTo(pH);
            keyPtArr.append(pH);
            path.lineTo(pD);
            keyPtArr.append(pD);
            path.lineTo(p0);
            keyPtArr.append(p0);

            res.append(keyPtArr);

            auto polygons = pathPolygons(path, 0.1);
            for (auto& polygon : polygons)
            {
                res.push_back(polygon.toList());
            }

            return res;
        }

        void TemplTypeReveseLie::setParams(const ProductParams& params)
        {
            OI_F(TemplTypeReveseLie);

            auto& dpArr = params.definitionPoints;
            if (dpArr.size() < 9)
            {
                return;
            }
            d->_a->setValue(dpArr[0].distance);
            d->_b->setValue(dpArr[1].distance);
            d->_c->setValue(dpArr[2].distance);
            d->_d->setValue(dpArr[3].distance);
            d->_e->setValue(dpArr[4].distance);
            d->_f->setValue(dpArr[5].distance);
            d->_g->setValue(dpArr[6].distance);
            d->_h->setValue(dpArr[7].distance);
            d->_i->setValue(dpArr[8].distance);
        }

        void TemplTypeReveseLie::updateParams(ProductParams& params)
        {
            params.definitionPoints = definitionPoints();
            params.profiles = parseProfile(params.definitionPoints);
            params.materialsThickness = 0;
            if (params.profiles.isEmpty() || params.profiles.first().isEmpty())
            {
                params.materialsWidth = 0;
            }
            else
            {
                auto& arr = params.profiles.first();
                float minVal = arr.first().x();
                float maxVal = arr.first().x();
                for (auto& val : arr)
                {
                    if (val.x() < minVal)
                    {
                        minVal = val.x();
                    }
                    if (val.x() > maxVal)
                    {
                        maxVal = val.x();
                    }
                }
                params.materialsWidth = std::abs(maxVal - minVal);
            }
        }


        class TemplTypeStandPartImpl :public Impl<TemplTypeStandPart>
        {
        public:
            void init();
            void exit();

        public:
            QFormLayout* _lay = nullptr;
            QDoubleSpinBox* _a = nullptr;
            QDoubleSpinBox* _b = nullptr;
            QDoubleSpinBox* _c = nullptr;
            QDoubleSpinBox* _d = nullptr;
            QDoubleSpinBox* _e = nullptr;
            QDoubleSpinBox* _f = nullptr;
            QDoubleSpinBox* _g = nullptr;
            QDoubleSpinBox* _h = nullptr;
            QDoubleSpinBox* _i = nullptr;
            QDoubleSpinBox* _j = nullptr;
            QDoubleSpinBox* _k = nullptr;
            QDoubleSpinBox* _l = nullptr;
            QTableWidget* _parts = nullptr;
            
            int _partNum = 20;
        };

        void TemplTypeStandPartImpl::init()
        {
            OI_Q(TemplTypeStandPart);
            _lay = new QFormLayout(q);
            q->setLayout(_lay);
            _a = lengthBox(q);
            _lay->addRow("A", _a);
            _b = lengthBox(q);
            _lay->addRow("B", _b);
            _c = lengthBox(q);
            _lay->addRow("C", _c);
            _d = angleBox(q);
            _lay->addRow("D", _d);
            _e = lengthBox(q);
            _lay->addRow("E", _e);
            _f = angleBox(q);
            _lay->addRow("F", _f);
            _g = lengthBox(q);
            _lay->addRow("G", _g);
            _h = lengthBox(q);
            _lay->addRow("H", _h);
            _i = lengthBox(q);
            _lay->addRow("I", _i);
            _j = lengthBox(q);
            _lay->addRow("J", _j);
            _k = lengthBox(q);
            _lay->addRow("K", _k);
            _l = lengthBox(q);
            _lay->addRow("L", _l);

            _parts = new QTableWidget(q);
            _lay->addRow("分段", _parts);
            QStringList headers;
            headers << "横向相对距离" << "纵向绝对高度";
            _parts->setColumnCount(headers.size());
            _parts->setHorizontalHeaderLabels(headers);

            _parts->setRowCount(_partNum);
            for (int i = 0; i < _partNum; i++)
            {
                _parts->setItem(i, 0, new QTableWidgetItem());
                _parts->setItem(i, 1, new QTableWidgetItem());
            }
        }

        void TemplTypeStandPartImpl::exit()
        {

        }

        TemplTypeStandPart::TemplTypeStandPart(DefinitionType dt, QWidget* parent)
            :TemplInputWidget(dt, parent)
        {
            OI_I(TemplTypeStandPart)->init();
        }

        TemplTypeStandPart::~TemplTypeStandPart()
        {
            OI_E(TemplTypeStandPart)->exit();
        }

        QList<DefinitionPoint> TemplTypeStandPart::definitionPoints()
        {
            OI_F(TemplTypeStandPart);

            QList<DefinitionPoint> dpArr;
            DefinitionPoint dp;
            dp.type = getDefinitionType();
            dp.distance = d->_a->value();
            dpArr.append(dp);
            dp.distance = d->_b->value();
            dpArr.append(dp);
            dp.distance = d->_c->value();
            dpArr.append(dp);
            dp.distance = d->_d->value();
            dpArr.append(dp);
            dp.distance = d->_e->value();
            dpArr.append(dp);
            dp.distance = d->_f->value();
            dpArr.append(dp);
            dp.distance = d->_g->value();
            dpArr.append(dp);
            dp.distance = d->_h->value();
            dpArr.append(dp);
            dp.distance = d->_i->value();
            dpArr.append(dp);
            dp.distance = d->_j->value();
            dpArr.append(dp);
            dp.distance = d->_k->value();
            dpArr.append(dp);
            dp.distance = d->_l->value();
            dpArr.append(dp);

            int rowCount = d->_parts->rowCount();
            float preDis = 0;
            for (int row = 0; row < rowCount; row++)
            {
                QString stringM = d->_parts->item(row, 0)->text();
                QString stringN = d->_parts->item(row, 1)->text();
                if (stringM.isEmpty() || stringN.isEmpty())
                {
                    break;
                }
                dp.distance = stringM.toFloat() + preDis;
                dpArr.append(dp);
                dp.distance = stringN.toFloat();
                dpArr.append(dp);

                preDis += stringM.toFloat();
            }
            return dpArr;
        }

        OiProfiles TemplTypeStandPart::parseProfile(const QList<DefinitionPoint>& dpArr)
        {
            OI_F(TemplTypeStandPart);

            QPointF p0(0, 0);
            double lVal = d->_l->value();
            QPointF p1(0, lVal);
            double kVal = d->_k->value();
            QPointF p2(kVal, 0);
            double aVal = d->_a->value();
            QPointF p3(kVal, aVal);

            double hVal = d->_h->value();
            double iVal = d->_i->value();
            QPointF pI(-iVal, 0);
            double eVal = d->_e->value();
            double ijH = std::sqrt(eVal * eVal - hVal * hVal);
            QPointF pJ(hVal - iVal, ijH);

            QPointF dirJI = pI - pJ;
            double dAngle = d->_d->value();
            QPointF dirJL = rotateVector(dirJI, dAngle, true);
            double cVal = d->_c->value();
            QPointF pL = pJ + dirJL * cVal;

            QPointF dirLM = rotateVector(dirJL, 90, true);
            double bVal = d->_b->value();
            QPointF pM = pL + dirLM * bVal;

            QPointF dirIJ = pJ - pI;
            double fAngle = d->_f->value();
            QPointF dirIN = rotateVector(dirIJ, fAngle, false);
            double jVal = d->_j->value();
            QPointF pN = pI + dirIN * jVal;

            QPointF dirNO = rotateVector(dirIN, 90, false);
            double gVal = d->_g->value();
            QPointF pO = pN + dirNO * gVal;

            QPainterPath path;
            OiProfiles res;
            OiProfile keyPtArr;

            path.moveTo(p0);
            keyPtArr.append(p0);
            path.lineTo(pO);
            keyPtArr.append(pO);
            path.lineTo(pN);
            keyPtArr.append(pN);
            path.lineTo(pI);
            keyPtArr.append(pI);
            path.lineTo(pJ);
            keyPtArr.append(pJ);
            path.lineTo(pL);
            keyPtArr.append(pL);
            path.lineTo(pM);
            keyPtArr.append(pM);
            path.lineTo(p1);
            keyPtArr.append(p1);

            int rowCount = d->_parts->rowCount();
            float preDis = 0;
            for (int row = 0; row < rowCount; row++)
            {
                QString stringM = d->_parts->item(row, 0)->text();
                QString stringN = d->_parts->item(row, 1)->text();
                if (stringM.isEmpty() || stringN.isEmpty())
                {
                    break;
                }

                QPointF pMN(stringM.toFloat() + preDis, stringN.toFloat());
                preDis += stringM.toFloat();
                path.lineTo(pMN);
                keyPtArr.append(pMN);
            }

            path.lineTo(p3);
            keyPtArr.append(p3);
            path.lineTo(p2);
            keyPtArr.append(p2);
            path.lineTo(p0);

            res.append(keyPtArr);

            auto polygons = pathPolygons(path, 0.1);
            for (auto& polygon : polygons)
            {
                res.push_back(polygon.toList());
            }

            return res;
        }

        void TemplTypeStandPart::setParams(const ProductParams& params)
        {
            OI_F(TemplTypeStandPart);

            auto& dpArr = params.definitionPoints;
            if (dpArr.size() < 12)
            {
                return;
            }
            int dpArrIndex = 0;
            d->_a->setValue(dpArr[dpArrIndex++].distance);
            d->_b->setValue(dpArr[dpArrIndex++].distance);
            d->_c->setValue(dpArr[dpArrIndex++].distance);
            d->_d->setValue(dpArr[dpArrIndex++].distance);
            d->_e->setValue(dpArr[dpArrIndex++].distance);
            d->_f->setValue(dpArr[dpArrIndex++].distance);
            d->_g->setValue(dpArr[dpArrIndex++].distance);
            d->_h->setValue(dpArr[dpArrIndex++].distance);
            d->_i->setValue(dpArr[dpArrIndex++].distance);
            d->_j->setValue(dpArr[dpArrIndex++].distance);
            d->_k->setValue(dpArr[dpArrIndex++].distance);
            d->_l->setValue(dpArr[dpArrIndex++].distance);
            
            for (int i = 0; i < d->_partNum; i++)
            {
                auto itemM = d->_parts->item(i, 0);
                auto itemN = d->_parts->item(i, 1);
                if (i * 2 + dpArrIndex + 1 < dpArr.size())
                {
                    itemM->setText(QString::number(dpArr[dpArrIndex + i * 2 + 0].distance - (i > 0 ? dpArr[dpArrIndex + (i - 1) * 2 + 0].distance : 0)));
                    itemN->setText(QString::number(dpArr[dpArrIndex + i * 2 + 1].distance));
                }
            }
        }

        void TemplTypeStandPart::updateParams(ProductParams& params)
        {
            params.definitionPoints = definitionPoints();
            params.profiles = parseProfile(params.definitionPoints);
            params.materialsThickness = 0;
            if (params.profiles.isEmpty() || params.profiles.first().isEmpty())
            {
                params.materialsWidth = 0;
            }
            else
            {
                auto& arr = params.profiles.first();
                float minVal = arr.first().x();
                float maxVal = arr.first().x();
                for (auto& val : arr)
                {
                    if (val.x() < minVal)
                    {
                        minVal = val.x();
                    }
                    if (val.x() > maxVal)
                    {
                        maxVal = val.x();
                    }
                }
                params.materialsWidth = std::abs(maxVal - minVal);
            }
        }

        class TemplTypeStandArcsImpl :public Impl<TemplTypeStandArcs>
        {
        public:
            void init();
            void exit();

        public:
            QFormLayout* _lay = nullptr;
            QDoubleSpinBox* _a = nullptr;
            QDoubleSpinBox* _b = nullptr;
            QDoubleSpinBox* _c = nullptr;
            QDoubleSpinBox* _d = nullptr;
            QDoubleSpinBox* _e = nullptr;
            QDoubleSpinBox* _f = nullptr;
            QDoubleSpinBox* _g = nullptr;
            QDoubleSpinBox* _h = nullptr;
            QDoubleSpinBox* _i = nullptr;
            QDoubleSpinBox* _j = nullptr;
            QDoubleSpinBox* _k = nullptr;
            QDoubleSpinBox* _l = nullptr;
        };

        void TemplTypeStandArcsImpl::init()
        {
            OI_Q(TemplTypeStandArcs);
            _lay = new QFormLayout(q);
            q->setLayout(_lay);
            _a = lengthBox(q);
            _lay->addRow("A", _a);
            _b = lengthBox(q);
            _lay->addRow("B", _b);
            _c = lengthBox(q);
            _lay->addRow("C", _c);
            _d = lengthBox(q);
            _lay->addRow("D", _d);
            _e = lengthBox(q);
            _lay->addRow("E", _e);
            _f = lengthBox(q);
            _lay->addRow("F", _f);
            _g = lengthBox(q);
            _lay->addRow("G", _g);
            _h = lengthBox(q);
            _lay->addRow("H", _h);
            _i = angleBox(q);
            _lay->addRow("I", _i);
            _j = lengthBox(q);
            _lay->addRow("J", _j);
            _k = lengthBox(q);
            _lay->addRow("K", _k);
            _l = lengthBox(q);
            _lay->addRow("L", _l);
        }

        void TemplTypeStandArcsImpl::exit()
        {

        }

        TemplTypeStandArcs::TemplTypeStandArcs(DefinitionType dt, QWidget* parent)
            :TemplInputWidget(dt, parent)
        {
            OI_I(TemplTypeStandArcs)->init();
        }

        TemplTypeStandArcs::~TemplTypeStandArcs()
        {
            OI_E(TemplTypeStandArcs)->exit();
        }

        QList<DefinitionPoint> TemplTypeStandArcs::definitionPoints()
        {
            OI_F(TemplTypeStandArcs);

            QList<DefinitionPoint> dpArr;
            DefinitionPoint dp;
            dp.type = getDefinitionType();
            dp.distance = d->_a->value();
            dpArr.append(dp);
            dp.distance = d->_b->value();
            dpArr.append(dp);
            dp.distance = d->_c->value();
            dpArr.append(dp);
            dp.distance = d->_d->value();
            dpArr.append(dp);
            dp.distance = d->_e->value();
            dpArr.append(dp);
            dp.distance = d->_f->value();
            dpArr.append(dp);
            dp.distance = d->_g->value();
            dpArr.append(dp);
            dp.distance = d->_h->value();
            dpArr.append(dp);
            dp.distance = d->_i->value();
            dpArr.append(dp);
            dp.distance = d->_j->value();
            dpArr.append(dp);
            dp.distance = d->_k->value();
            dpArr.append(dp);
            dp.distance = d->_l->value();
            dpArr.append(dp);
            return dpArr;
        }

        OiProfiles TemplTypeStandArcs::parseProfile(const QList<DefinitionPoint>& dpArr)
        {
            OI_F(TemplTypeStandArcs);

            double aVal = d->_a->value();
            double bVal = d->_b->value();
            double cVal = d->_c->value();
            double dVal = d->_d->value();

            QPointF p0(0, 0);
            QPointF p1(aVal - bVal, 0);
            QPointF p2(p1.x(), cVal);
            QPointF p3(0, dVal);

            QPointF pB(-bVal, 0);
            double eVal = d->_e->value();
            double fVal = d->_f->value();
            double feH = std::sqrt(fVal * fVal - eVal * eVal);
            QPointF pE(pB.x() + eVal, feH);
            QPointF dirBE = pE - pB;
            dirBE /= normQPointF(dirBE);

            double gVal = d->_g->value();
            QPointF pG = pB + dirBE * gVal;
            double hVal = d->_h->value();
            QPointF pH = pG + dirBE * hVal;

            QPointF dirEB = -dirBE;
            double iVal = d->_i->value();
            double jVal = d->_j->value();
            QPointF dirHJ = rotateVector(dirEB, iVal, true);
            QPointF pJ = pH + dirHJ * jVal;
            QPointF dirJK = rotateVector(dirHJ, 90, true);
            double kVal = d->_k->value();
            QPointF pK = pJ + dirJK * kVal;

            QPointF dirGJ = rotateVector(dirBE, iVal, false);
            QPointF pJ2 = pG + dirGJ * gVal;
            QPointF dirJK2 = rotateVector(dirGJ, 90, false);
            QPointF pK2 = pJ2 + dirJK2 * kVal;

            OiProfiles res;
            QPainterPath path;
            OiProfile keyPtArr;

            path.moveTo(p0);
            keyPtArr.append(p0);
            path.lineTo(p1);
            keyPtArr.append(p1);
            path.lineTo(p2);
            keyPtArr.append(p2);
            double lVal = d->_l->value();
            QPointF c = findCircleCenter(p2, p3, -lVal);
            QRectF arcRect(QPointF(c.x() - lVal, c.y() - lVal), QSizeF(2 * lVal, 2 * lVal));
            QPointF dir(1, 0);
            QPointF c1 = p2 - c;
            QPointF c2 = p3 - c;
            double cs1 = QPointF::dotProduct(c1, dir) / normQPointF(c1);
            double cs2 = QPointF::dotProduct(c2, dir) / normQPointF(c2);
            double ac1 = std::acos(cs1);
            double ac2 = std::acos(cs2);
            ac1 = 2 * M_PI - ac1;
            ac2 = 2 * M_PI - ac2;
            double begAng = 360 - std::max(ac1, ac2) * 180.0 / M_PI;
            double swpAng = (360 - std::min(ac1, ac2) * 180.0 / M_PI) - begAng;
            path.arcTo(arcRect, begAng, swpAng);
            keyPtArr.append(p3);
            path.lineTo(pK);
            keyPtArr.append(pK);
            path.lineTo(pJ);
            keyPtArr.append(pJ);
            path.lineTo(pH);
            keyPtArr.append(pH);
            path.lineTo(pG);
            keyPtArr.append(pG);
            path.lineTo(pJ2);
            keyPtArr.append(pJ2);
            path.lineTo(pK2);
            keyPtArr.append(pK2);
            path.lineTo(p0);

            res.append(keyPtArr);

            auto polygons = pathPolygons(path, 0.1);
            for (auto& polygon : polygons)
            {
                res.push_back(polygon.toList());
            }

            return res;
        }

        void TemplTypeStandArcs::setParams(const ProductParams& params)
        {
            OI_F(TemplTypeStandArcs);

            auto& dpArr = params.definitionPoints;
            if (dpArr.size() < 12)
            {
                return;
            }
            d->_a->setValue(dpArr[0].distance);
            d->_b->setValue(dpArr[1].distance);
            d->_c->setValue(dpArr[2].distance);
            d->_d->setValue(dpArr[3].distance);
            d->_e->setValue(dpArr[4].distance);
            d->_f->setValue(dpArr[5].distance);
            d->_g->setValue(dpArr[6].distance);
            d->_h->setValue(dpArr[7].distance);
            d->_i->setValue(dpArr[8].distance);
            d->_j->setValue(dpArr[9].distance);
            d->_k->setValue(dpArr[10].distance);
            d->_l->setValue(dpArr[11].distance);
        }

        void TemplTypeStandArcs::updateParams(ProductParams& params)
        {
            params.definitionPoints = definitionPoints();
            params.profiles = parseProfile(params.definitionPoints);
            params.materialsThickness = 0;
            if (params.profiles.isEmpty() || params.profiles.first().isEmpty())
            {
                params.materialsWidth = 0;
            }
            else
            {
                auto& arr = params.profiles.first();
                float minVal = arr.first().x();
                float maxVal = arr.first().x();
                for (auto& val : arr)
                {
                    if (val.x() < minVal)
                    {
                        minVal = val.x();
                    }
                    if (val.x() > maxVal)
                    {
                        maxVal = val.x();
                    }
                }
                params.materialsWidth = std::abs(maxVal - minVal);
            }
        }


        class TriBoardInputImpl :public Impl<TriBoardInput>
        {
        public:
            void init();
            void exit();

        public:
            QFormLayout* _lay = nullptr;
            QDoubleSpinBox* _a = nullptr;
            QDoubleSpinBox* _b = nullptr;
            QDoubleSpinBox* _c = nullptr;
            QDoubleSpinBox* _d = nullptr;
            QDoubleSpinBox* _e = nullptr;
            QDoubleSpinBox* _f = nullptr;
            QDoubleSpinBox* _g = nullptr;
            QDoubleSpinBox* _h = nullptr;
            QDoubleSpinBox* _i = nullptr;
            QDoubleSpinBox* _j = nullptr;
        };

        void TriBoardInputImpl::init()
        {
            OI_Q(TriBoardInput);
            _lay = new QFormLayout(q);
            q->setLayout(_lay);
            _a = lengthBox(q);
            _lay->addRow("A", _a);
            _b = lengthBox(q);
            _lay->addRow("B", _b);
            _c = lengthBox(q);
            _lay->addRow("C", _c);
            _d = lengthBox(q);
            _lay->addRow("D", _d);
            _e = lengthBox(q);
            _lay->addRow("E", _e);
            _f = lengthBox(q);
            _lay->addRow("F", _f);
            _g = lengthBox(q);
            _lay->addRow("G", _g);
            _h = lengthBox(q);
            _lay->addRow("H", _h);
            _i = lengthBox(q);
            _lay->addRow("I", _i);
            _j = lengthBox(q);
            _lay->addRow("J", _j);
        }

        void TriBoardInputImpl::exit()
        {

        }

        TriBoardInput::TriBoardInput(DefinitionType dt, QWidget* parent)
            :TemplInputWidget(dt, parent)
        {
            OI_I(TriBoardInput)->init();
        }

        TriBoardInput::~TriBoardInput()
        {
            OI_E(TriBoardInput)->exit();
        }

        QList<DefinitionPoint> TriBoardInput::definitionPoints()
        {
            OI_F(TriBoardInput);

            QList<DefinitionPoint> dpArr;
            DefinitionPoint dp;
            //dp.type = DT_TriBoard;
            dp.distance = d->_a->value();
            dpArr.append(dp);
            dp.distance = d->_b->value();
            dpArr.append(dp);
            dp.distance = d->_c->value();
            dpArr.append(dp);
            dp.distance = d->_d->value();
            dpArr.append(dp);
            dp.distance = d->_e->value();
            dpArr.append(dp);
            dp.distance = d->_f->value();
            dpArr.append(dp);
            dp.distance = d->_g->value();
            dpArr.append(dp);
            dp.distance = d->_h->value();
            dpArr.append(dp);
            dp.distance = d->_i->value();
            dpArr.append(dp);
            dp.distance = d->_j->value();
            dpArr.append(dp);
            return dpArr;
        }

        OiProfiles TriBoardInput::parseProfile(const QList<DefinitionPoint>& dpArr)
        {
            OI_F(TriBoardInput);

            double aVal = d->_a->value();
            double bVal = d->_b->value();
            double cVal = d->_c->value();
            double dVal = d->_d->value();
            double eVal = d->_e->value();
            double fVal = d->_f->value();
            double gVal = d->_g->value();
            double hVal = d->_h->value();
            double iVal = d->_i->value();
            double jVal = d->_j->value();

            QPointF p0(0, 0);
            QPointF p1(aVal, 0);
            QPointF p2(p1.x(), cVal);
            QPointF p3(p2.x() - dVal, p2.y());
            QPointF p4(bVal, eVal);
            QPointF p5(fVal, gVal);
            QPointF p6(0, gVal);

            QPointF center(-iVal, hVal);
            float y1 = center.y() + std::sqrt(jVal * jVal - center.x() * center.x());
            float y2 = center.y() - std::sqrt(jVal * jVal - center.x() * center.x());
            QPointF p7(0, y1);
            QPointF p8(0, y2);
            QPointF dir(1, 0);
            QPointF dirC1 = p7 - center;
            QPointF dirC2 = p8 - center;

            double cs1 = QPointF::dotProduct(dirC1, dir) / normQPointF(dirC1);
            double cs2 = QPointF::dotProduct(dirC2, dir) / normQPointF(dirC2);
            double ac1 = std::acos(cs1);
            double ac2 = std::acos(cs2);
            double ag1 = (ac1 * 180.0) / M_PI;
            double ag2 = (ac2 * 180.0) / M_PI;
            QRectF arcRect(QPointF(center.x() - jVal, center.y() - jVal), QSizeF(2 * jVal, 2 * jVal));

            OiProfiles res;
            QPainterPath path;
            OiProfile keyPtArr;

            path.moveTo(p0);
            keyPtArr.append(p0);
            path.lineTo(p1);
            keyPtArr.append(p1);
            path.lineTo(p2);
            keyPtArr.append(p2);
            path.lineTo(p3);
            keyPtArr.append(p3);
            path.lineTo(p4);
            keyPtArr.append(p4);
            path.lineTo(p5);
            keyPtArr.append(p5);
            path.lineTo(p6);
            keyPtArr.append(p6);
            path.lineTo(p7);
            keyPtArr.append(p7);
            float begAng = -ag1;
            float swpAng = ag1 + ag2;
            path.arcTo(arcRect, begAng, swpAng);
            keyPtArr.append(p8);
            path.lineTo(p0);

            res.append(keyPtArr);

            auto polygons = pathPolygons(path, 0.1);
            for (auto& polygon : polygons)
            {
                res.push_back(polygon.toList());
            }

            return res;
        }

        void TriBoardInput::setParams(const ProductParams& params)
        {
            OI_F(TriBoardInput);

            auto& dpArr = params.definitionPoints;
            if (dpArr.size() < 10)
            {
                return;
            }
            d->_a->setValue(dpArr[0].distance);
            d->_b->setValue(dpArr[1].distance);
            d->_c->setValue(dpArr[2].distance);
            d->_d->setValue(dpArr[3].distance);
            d->_e->setValue(dpArr[4].distance);
            d->_f->setValue(dpArr[5].distance);
            d->_g->setValue(dpArr[6].distance);
            d->_h->setValue(dpArr[7].distance);
            d->_i->setValue(dpArr[8].distance);
            d->_j->setValue(dpArr[9].distance);
        }

        void TriBoardInput::updateParams(ProductParams& params)
        {
            params.definitionPoints = definitionPoints();
            params.profiles = parseProfile(params.definitionPoints);
            params.materialsThickness = 0;
            if (params.profiles.isEmpty() || params.profiles.first().isEmpty())
            {
                params.materialsWidth = 0;
            }
            else
            {
                auto& arr = params.profiles.first();
                float minVal = arr.first().x();
                float maxVal = arr.first().x();
                for (auto& val : arr)
                {
                    if (val.x() < minVal)
                    {
                        minVal = val.x();
                    }
                    if (val.x() > maxVal)
                    {
                        maxVal = val.x();
                    }
                }
                params.materialsWidth = std::abs(maxVal - minVal);
            }
        }


    }
}

